/*
 * Copyright 2020 Mike Penz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mikepenz.iconics;

import ohos.agp.components.Component;
import ohos.agp.components.ComponentState;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.BlendMode;
import ohos.agp.render.BlurDrawLooper;
import ohos.agp.render.Canvas;
import ohos.agp.render.ColorFilter;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.render.Texture;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.global.resource.solidxml.Theme;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;

import com.mikepenz.iconics.animation.IconicsAnimatedDrawable;
import com.mikepenz.iconics.typeface.IIcon;
import com.mikepenz.iconics.typeface.ITypeface;
import com.mikepenz.iconics.utils.KtExtensions;

import java.util.HashMap;
import java.util.Map;

/**
 * A custom [Drawable] which can display icons from icon fonts.
 */
public class IconicsDrawable extends ShapeElement implements Component.DrawTask {
    private static final String TAG = "IconicsDrawable";
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 110, TAG);
    private static final String ERROR_MESSAGE = "Iconics.init() not yet executed, icon will be missing";
    private static final int DEFAULT_ALPHA = 255;
    private static final int DEFAULT_SIZE = -1;
    private static final float DEFAULT_SIZE_2 = -1f;
    private final Component component;
    private Context context;
    private final IconicsBrush<Paint> iconBrush;
    private final IconicsBrush<Paint> backgroundContourBrush;
    private final IconicsBrush<Paint> backgroundBrush;
    private final IconicsBrush<Paint> contourBrush;
    private Theme theme;
    private Rect canvasRect = null;
    private int state;
    private final Rect paddingBounds = new Rect();
    private final RectFloat pathBounds = new RectFloat();
    private final Path path = new Path();
    private int compatAlpha = DEFAULT_ALPHA;
    private IIcon icon = null;
    private String iconText = null;
    private boolean autoMirroredCompat = false;
    private boolean invalidationEnabled = true;
    private boolean invalidateShadowEnabled = true;
    private int sizeXpx = DEFAULT_SIZE;
    private int sizeYpx = DEFAULT_SIZE;
    private boolean respectFontBounds = Iconics.getInstance().isRespectFontBoundsDefault();
    private boolean drawContour = false;
    private boolean drawBackgroundContour = false;
    private float roundedCornerRxPx = DEFAULT_SIZE_2;
    private float roundedCornerRyPx = DEFAULT_SIZE_2;
    private int paddingPx = 0;
    private int contourWidthPx = 0;
    private int backgroundContourWidthPx = 0;
    private int iconOffsetXpx = 0;
    private int iconOffsetYpx = 0;
    private float shadowRadiusPx = 0f;
    private float shadowDxPx = 0f;
    private float shadowDyPx = 0f;
    private Color shadowColorInt = Color.TRANSPARENT;
    private Map<Integer, Color> tint;
    private BlendMode tintPorterMode = BlendMode.SRC_IN;
    private ColorFilter tintFilter;
    private ColorFilter iconColorFilter;

    /**
     * 构造方法
     *
     * @param component 绘制的组件
     */
    public IconicsDrawable(Component component) {
        this.component = component;
        if (component != null) {
            context = component.getContext();
        }
        Paint icon1 = new Paint();
        icon1.setAntiAlias(true);
        iconBrush = new IconicsBrush<>(icon1);
        Paint backgroundContour = new Paint();
        backgroundContour.setAntiAlias(true);
        backgroundContourBrush = new IconicsBrush<>(backgroundContour);
        Paint background = new Paint();
        background.setAntiAlias(true);
        backgroundBrush = new IconicsBrush<>(background);
        Paint contour = new Paint();
        contour.setAntiAlias(true);
        contourBrush = new IconicsBrush<>(contour);
        init();
    }

    /**
     * 构造方法
     *
     * @param component 绘制的组件
     * @param theme 主题
     */
    public IconicsDrawable(Component component, Theme theme) {
        this(component);
        this.theme = theme;
    }

    /**
     * 构造方法
     *
     * @param component 绘制的组件
     * @param context 上下文
     */
    public IconicsDrawable(Component component, Context context) {
        this(component);
        this.context = context;
        Iconics.getInstance().init(context);
    }

    /**
     * 构造方法
     *
     * @param component 绘制的组件
     * @param context 上下文
     * @param icon 字体图标
     */
    public IconicsDrawable(Component component, Context context, Character icon) {
        this(component);
        this.context = context;
        Iconics.getInstance().init(context);
        IconicsDrawable icon1 = icon(icon);
        icon1.toString();
    }

    /**
     * 构造方法
     *
     * @param component 绘制的组件
     * @param context 上下文
     * @param icon 字体图标
     */
    public IconicsDrawable(Component component, Context context, String icon) {
        this(component);
        this.context = context;
        Iconics.getInstance().init(context);
        IconicsDrawable icon1 = icon(icon);
        icon1.toString();
    }

    /**
     * 构造方法
     *
     * @param component 绘制的组件
     * @param context 上下文
     * @param icon 字体图标
     */
    public IconicsDrawable(Component component, Context context, IIcon icon) {
        this(component);
        this.context = context;
        Iconics.getInstance().init(context);
        IconicsDrawable icon1 = icon(icon);
        icon1.toString();
    }

    /**
     * 构造方法
     *
     * @param component 绘制的组件
     * @param context 上下文
     * @param typeface 字体文件
     * @param icon 字体图标
     */
    public IconicsDrawable(Component component, Context context, ITypeface typeface, IIcon icon) {
        this(component);
        this.context = context;
        Iconics.getInstance().init(context);
        IconicsDrawable icon1 = icon(typeface, icon);
        icon1.toString();
    }

    /**
     * 设置图标
     *
     * @param icon 字体图标
     */
    public void setIcon(Character icon) {
        Iconics.getInstance().init(context);
        IconicsDrawable icon1 = icon(icon);
        icon1.toString();
    }

    /**
     * 设置图标
     *
     * @param icon 字体图标
     */
    public void setIcon(String icon) {
        Iconics.getInstance().init(context);
        IconicsDrawable icon1 = icon(icon);
        icon1.toString();
    }

    /**
     * 设置图标
     *
     * @param icon 图标
     */
    public void setIcon(IIcon icon) {
        this.icon = icon;
        setTypeface(icon.getTypeface().getRawTypeface(getContext()));
        if (this.icon != null) {
            this.iconText = null;
            invalidateThis();
        }
    }

    /**
     * 获取icon画笔
     *
     * @return icon画笔
     */
    public IconicsBrush<Paint> getIconBrush() {
        return iconBrush;
    }

    /**
     * 获取背景线框画笔
     *
     * @return 背景线框画笔
     */
    public IconicsBrush<Paint> getBackgroundContourBrush() {
        return backgroundContourBrush;
    }

    /**
     * 获取背景画笔
     *
     * @return 背景画笔
     */
    public IconicsBrush<Paint> getBackgroundBrush() {
        return backgroundBrush;
    }

    /**
     * 获取icon线框画笔
     *
     * @return icon线框画笔
     */
    public IconicsBrush<Paint> getContourBrush() {
        return contourBrush;
    }

    /**
     * 获取上下文
     *
     * @return 上下文
     */
    private Context getContext() {
        return context;
    }

    /**
     * 获取当前绘制组件
     *
     * @return 绘制组件
     */
    public Component getComponent() {
        return component;
    }

    /**
     * 设置上下文
     *
     * @param context 上下文
     */
    public void setContext(Context context) {
        this.context = context;
    }

    /**
     * 获取当前状态
     *
     * @return 当前状态
     */
    public int getState() {
        return state;
    }

    /**
     * 设置图标颜色
     *
     * @param color 颜色
     */
    public void setColor(Color color) {
        setColorList(colorStateListValueOf(color));
    }

    /**
     * Set the color of the drawable.
     *
     * @param colorList 状态颜色对
     */
    public void setColorList(Map<Integer, Color> colorList) {
        iconBrush.setColorsList(colorList);
        if (iconBrush.applyState(state)) {
            invalidateThis();
        }
    }

    /**
     * 获取当前状态颜色对
     *
     * @return 状态颜色对
     */
    public Map<Integer, Color> getColorList() {
        return iconBrush.getColorsList();
    }

    /**
     * Set the style for the icon brush
     *
     * @param style 画笔样式
     */
    public void setStyle(Paint.Style style) {
        iconBrush.getPaint().setStyle(style);
        invalidateThis();
    }

    /**
     * 获取当前icon画笔样式
     *
     * @return icon画笔样式
     */
    public Paint.Style getStyle() {
        return iconBrush.getPaint().getStyle();
    }

    /**
     * Set the typeface of the drawable NOTE THIS WILL OVERWRITE THE ICONFONT!
     *
     * @param font 字体
     */
    public void setTypeface(Font font) {
        iconBrush.getPaint().setFont(font);
        invalidateThis();
    }

    /**
     * 获取当前字体
     *
     * @return 当前字体
     */
    public Font getTypeface() {
        return iconBrush.getPaint().getFont();
    }

    /**
     * 设置背景线框颜色
     *
     * @param color 颜色
     */
    public void setBackgroundContourColor(Color color) {
        setBackgroundContourColorList(colorStateListValueOf(color));
    }

    /**
     * Set the color of the drawable.
     *
     * @param colorList 颜色
     */
    public void setBackgroundContourColorList(Map<Integer, Color> colorList) {
        backgroundContourBrush.setColorsList(colorList);
        if (backgroundContourBrush.applyState(state)) {
            invalidateThis();
        }
    }

    /**
     * 获取当前背景线框状态颜色对
     *
     * @return 背景线框状态颜色对
     */
    public Map<Integer, Color> getBackgroundContourColorList() {
        return backgroundContourBrush.getColorsList();
    }

    /**
     * 设置背景颜色
     *
     * @param color 颜色
     */
    public void setBackgroundColor(Color color) {
        setBackgroundColorList(colorStateListValueOf(color));
    }

    /**
     * Set the color of the drawable.
     *
     * @param colorList 颜色
     */
    public void setBackgroundColorList(Map<Integer, Color> colorList) {
        backgroundBrush.setColorsList(colorList);

        // disable invalidation temporary
        boolean enabled = invalidationEnabled;
        invalidationEnabled = false;
        final float dfValue = -1f;
        if (roundedCornerRxPx == dfValue) {
            roundedCornerRxPx = 0f;
        }
        if (roundedCornerRyPx == dfValue) {
            roundedCornerRyPx = 0f;
        }
        invalidationEnabled = enabled;
        if (backgroundBrush.applyState(state)) {
            invalidateThis();
        }
    }

    /**
     * 获取当前背景状态颜色对
     *
     * @return 背景状态颜色对
     */
    public Map<Integer, Color> getBackgroundColorList() {
        return backgroundBrush.getColorsList();
    }

    /**
     * 设置图标线框颜色
     *
     * @param color 颜色
     */
    public void setContourColor(Color color) {
        setContourColorList(colorStateListValueOf(color));
    }

    /**
     * Set the color of the drawable.
     *
     * @param colorList 颜色
     */
    public void setContourColorList(Map<Integer, Color> colorList) {
        contourBrush.setColorsList(colorList);
        if (contourBrush.applyState(state)) {
            invalidateThis();
        }
    }

    /**
     * 获取当前icon线框状态颜色对
     *
     * @return icon线框状态颜色对
     */
    public Map<Integer, Color> getContourColorList() {
        return contourBrush.getColorsList();
    }

    /**
     * Set the opacity
     *
     * @param compatAlpha 透明度
     */
    public void setCompatAlpha(int compatAlpha) {
        this.compatAlpha = compatAlpha;
        invalidateThis();
    }

    /**
     * 获取当前透明度
     *
     * @return 透明度
     */
    public int getCompatAlpha() {
        return compatAlpha;
    }

    /**
     * 获取当前图标
     *
     * @return the IIcon which is used inside this IconicsDrawable
     */
    public IIcon getIcon() {
        return icon;
    }

    /**
     * 设置图标文字
     *
     * @param iconText 图标文字
     */
    public void setIconText(String iconText) {
        this.iconText = iconText;
        if (this.iconText != null) {
            this.icon = null;
            invalidateThis();
        }
    }

    /**
     * 获取当前icon文字
     *
     * @return the PlainIcon which is used inside this IconicsDrawable
     */
    public String getIconText() {
        return iconText;
    }

    /**
     * 设置是否镜像显示
     *
     * @param autoMirroredCompat 是否镜像
     */
    public void setAutoMirroredCompat(boolean autoMirroredCompat) {
        this.autoMirroredCompat = autoMirroredCompat;
        invalidateThis();
    }

    /**
     * 当前是否镜像显示
     *
     * @return if auto mirroring is enabled for this drawable
     */
    public boolean isAutoMirroredCompat() {
        return autoMirroredCompat;
    }

    /**
     * 设置是否可刷新
     *
     * @param invalidationEnabled 是否可刷新
     */
    public void setInvalidationEnabled(boolean invalidationEnabled) {
        this.invalidationEnabled = invalidationEnabled;
        invalidateSelf();
    }

    /**
     * defines if we want to instantly invalidate on changes
     *
     * @return 是否可刷新
     */
    public boolean isInvalidationEnabled() {
        return invalidationEnabled;
    }

    /**
     * 设置尺寸
     *
     * @param size 尺寸大小
     */
    public void setSize(int size) {
        setSizeXpx(size);
        setSizeYpx(size);
    }

    /**
     * Set the size by X axis of the drawable.
     *
     * @param size icon宽度
     */
    public void setSizeXpx(int size) {
        this.sizeXpx = size;
        setBounds(0, 0, size, sizeYpx);
    }

    /**
     * 获取icon当前宽度
     *
     * @return icon宽度
     */
    public int getSizeXpx() {
        return sizeXpx;
    }

    /**
     * Set the size by Y axis of the drawable.
     *
     * @param size icon高度
     */
    public void setSizeYpx(int size) {
        this.sizeYpx = size;
        setBounds(0, 0, sizeXpx, size);
    }

    /**
     * 获取icon当前高度
     *
     * @return icon高度
     */
    public int getSizeYpx() {
        return sizeYpx;
    }

    /**
     * 设置是否适应字体边距
     *
     * @param respectFontBounds
     */
    public void setRespectFontBounds(boolean respectFontBounds) {
        this.respectFontBounds = respectFontBounds;
        invalidateThis();
    }

    /**
     * 是否适应字体边距
     *
     * @return 是否适应字体边距
     */
    public boolean isRespectFontBounds() {
        return respectFontBounds;
    }

    /**
     * 设置是否绘制边框
     *
     * @param drawContour 是否绘制边框
     */
    public void setDrawContour(boolean drawContour) {
        if (this.drawContour != drawContour) {
            this.drawContour = drawContour;
            int temp;
            if (drawContour) {
                temp = 1;
            } else {
                temp = -1;
            }
            paddingPx += temp * contourWidthPx;
            invalidateThis();
        }
    }

    /**
     * 是否绘制边框
     *
     * @return 是否绘制边框
     */
    public boolean isDrawContour() {
        return drawContour;
    }

    /**
     * 设置是否绘制背景边框
     *
     * @param drawBackgroundContour 是否绘制背景边框
     */
    public void setDrawBackgroundContour(boolean drawBackgroundContour) {
        if (this.drawBackgroundContour != drawBackgroundContour) {
            this.drawBackgroundContour = drawBackgroundContour;
            int temp;
            if (drawBackgroundContour) {
                temp = 1;
            } else {
                temp = -1;
            }
            paddingPx += temp * backgroundContourWidthPx * 2;
            invalidateThis();
        }
    }

    /**
     * 是否绘制背景边框
     *
     * @return 是否绘制背景边框
     */
    public boolean isDrawBackgroundContour() {
        return drawBackgroundContour;
    }

    /**
     * 设置圆角
     *
     * @param size 圆角大小
     */
    public void setRoundCornerSize(float size) {
        setRoundedCornerRxPx(size);
        setRoundedCornerRyPx(size);
    }

    /**
     * 设置x圆角大小
     *
     * @param roundedCornerRxPx 圆角大小
     */
    public void setRoundedCornerRxPx(float roundedCornerRxPx) {
        this.roundedCornerRxPx = roundedCornerRxPx;
        invalidateThis();
    }

    /**
     * 获取x圆角大小
     *
     * @return x圆角大小
     */
    public float getRoundedCornerRxPx() {
        return roundedCornerRxPx;
    }

    /**
     * 设置y圆角大小
     *
     * @param roundedCornerRyPx 圆角大小
     */
    public void setRoundedCornerRyPx(float roundedCornerRyPx) {
        this.roundedCornerRyPx = roundedCornerRyPx;
        invalidateThis();
    }

    /**
     * 获取y圆角大小
     *
     * @return y圆角大小
     */
    public float getRoundedCornerRyPx() {
        return roundedCornerRyPx;
    }

    /**
     * 设置icon内边距
     *
     * @param paddingPx icon内边距
     */
    public void setPaddingPx(int paddingPx) {
        if (this.paddingPx != paddingPx) {
            int adjustedPadding = paddingPx;
            if (drawContour) {
                adjustedPadding += contourWidthPx;
            }
            if (drawBackgroundContour) {
                adjustedPadding += backgroundContourWidthPx;
            }
            this.paddingPx = adjustedPadding;
            invalidateThis();
        }
    }

    /**
     * 获取icon内边距
     *
     * @return icon内边距
     */
    public int getPaddingPx() {
        return paddingPx;
    }

    /**
     * 设置icon线框宽度
     *
     * @param contourWidthPx icon线框宽度
     */
    public void setContourWidthPx(int contourWidthPx) {
        this.contourWidthPx = contourWidthPx;
        contourBrush.getPaint().setStrokeWidth(contourWidthPx);
        drawContour = true;
        invalidateThis();
    }

    /**
     * 获取icon线框宽度
     *
     * @return icon线框宽度
     */
    public int getContourWidthPx() {
        return contourWidthPx;
    }

    /**
     * 设置背景线框宽度
     *
     * @param backgroundContourWidthPx 背景线框宽度
     */
    public void setBackgroundContourWidthPx(int backgroundContourWidthPx) {
        this.backgroundContourWidthPx = backgroundContourWidthPx;
        backgroundContourBrush.getPaint().setStrokeWidth(backgroundContourWidthPx);
        drawBackgroundContour = true;
        invalidateThis();
    }

    /**
     * 获取背景线框宽度
     *
     * @return 背景线框宽度
     */
    public int getBackgroundContourWidthPx() {
        return backgroundContourWidthPx;
    }

    /**
     * 设置x偏移量
     *
     * @param offset 偏移量
     */
    public void setIconOffsetXpx(int offset) {
        this.iconOffsetXpx = offset;
        invalidateThis();
    }

    /**
     * 获取x偏移量
     *
     * @return 偏移量
     */
    public int getIconOffsetXpx() {
        return iconOffsetXpx;
    }

    /**
     * 设置y偏移量
     *
     * @param offset 偏移量
     */
    public void setIconOffsetYpx(int offset) {
        this.iconOffsetYpx = offset;
        invalidateThis();
    }

    /**
     * 获取y偏移量
     *
     * @return 偏移量
     */
    public int getIconOffsetYpx() {
        return iconOffsetYpx;
    }

    /**
     * 设置阴影圆角
     *
     * @param shadowRadiusPx 圆角大小
     */
    public void setShadowRadiusPx(float shadowRadiusPx) {
        this.shadowRadiusPx = shadowRadiusPx;
        updateShadow();
    }

    /**
     * 获取阴影圆角
     *
     * @return 阴影圆角
     */
    public float getShadowRadiusPx() {
        return shadowRadiusPx;
    }

    /**
     * 设置阴影x圆角
     *
     * @param shadowDxPx 圆角
     */
    public void setShadowDxPx(float shadowDxPx) {
        this.shadowDxPx = shadowDxPx;
        updateShadow();
    }

    /**
     * 获取阴影x圆角
     *
     * @return 阴影x圆角
     */
    public float getShadowDxPx() {
        return shadowDxPx;
    }

    /**
     * 设置阴影y圆角
     *
     * @param shadowDyPx 圆角
     */
    public void setShadowDyPx(float shadowDyPx) {
        this.shadowDyPx = shadowDyPx;
        updateShadow();
    }

    /**
     * 获取阴影y圆角
     *
     * @return 阴影y圆角
     */
    public float getShadowDyPx() {
        return shadowDyPx;
    }

    /**
     * 删除阴影
     */
    public void clearShadow() {
        iconBrush.getPaint().clearBlurDrawLooper();
        invalidateThis();
    }

    /**
     * 设置阴影颜色
     *
     * @param shadowColorInt 阴影颜色
     */
    public void setShadowColorInt(Color shadowColorInt) {
        this.shadowColorInt = shadowColorInt;
        updateShadow();
        invalidateThis();
    }

    /**
     * 获取阴影颜色
     *
     * @return 阴影颜色
     */
    public Color getShadowColorInt() {
        return shadowColorInt;
    }

    /**
     * 更新阴影
     */
    private void updateShadow() {
        // will not update shadows if dinvalidation is disabled
        if (invalidateShadowEnabled) {
            BlurDrawLooper blurDrawLooper = new BlurDrawLooper(shadowRadiusPx, shadowDxPx, shadowDyPx, shadowColorInt);
            iconBrush.getPaint().setBlurDrawLooper(blurDrawLooper);
            invalidateThis();
        }
    }

    /**
     * 设置着色
     *
     * @param tint 颜色对
     */
    public void setTint(Map<Integer, Color> tint) {
        this.tint = tint;
        updateTintFilter();
        invalidateThis();
    }

    /**
     * 获取当前着色
     *
     * @return 当前着色对
     */
    public Map<Integer, Color> getTint() {
        return tint;
    }

    /**
     * 设置混合模式
     *
     * @param tintPorterMode 混合模式
     */
    public void setTintPorterMode(BlendMode tintPorterMode) {
        this.tintPorterMode = tintPorterMode;
        updateTintFilter();
        invalidateThis();
    }

    /**
     * 获取当前混合模式
     *
     * @return 当前混合模式
     */
    public BlendMode getTintPorterMode() {
        return tintPorterMode;
    }

    /**
     * 设置icon颜色过滤器
     *
     * @param iconColorFilter 颜色过滤器
     */
    public void setIconColorFilter(ColorFilter iconColorFilter) {
        this.iconColorFilter = iconColorFilter;
        invalidateThis();
    }

    /**
     * 获取icon颜色过滤器
     *
     * @return icon颜色过滤器
     */
    public ColorFilter getIconColorFilter() {
        return iconColorFilter;
    }

    /**
     * 构造状态颜色对
     *
     * @param color 颜色值
     * @return 状态颜色对
     */
    public Map<Integer, Color> colorStateListValueOf(Color color) {
        Map<Integer, Color> colorList = new HashMap<>();
        colorList.put(ComponentState.COMPONENT_STATE_CHECKED, color);
        colorList.put(ComponentState.COMPONENT_STATE_DISABLED, color);
        colorList.put(ComponentState.COMPONENT_STATE_EMPTY, color);
        colorList.put(ComponentState.COMPONENT_STATE_FOCUSED, color);
        colorList.put(ComponentState.COMPONENT_STATE_HOVERED, color);
        colorList.put(ComponentState.COMPONENT_STATE_PRESSED, color);
        colorList.put(ComponentState.COMPONENT_STATE_SELECTED, color);
        return colorList;
    }

    private void init() {
        KtExtensions.also(iconBrush, it -> {
            it.setColorsList(colorStateListValueOf(Color.BLACK));
            KtExtensions.apply(it.getPaint(), paint -> {
                paint.setStyle(Paint.Style.FILL_STYLE);
                paint.setTextAlign(TextAlignment.CENTER);
                paint.setUnderLine(false);
            });
        });
        contourBrush.getPaint().setStyle(Paint.Style.STROKE_STYLE);
        backgroundContourBrush.getPaint().setStyle(Paint.Style.STROKE_STYLE);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        draw(canvas);
    }

    /**
     * 绘制icon
     *
     * @param canvas 画布
     */
    protected void draw(Canvas canvas) {
        if (icon == null && iconText == null) {
            return;
        }
        final Rect viewBounds = getBounds();
        updatePaddingBounds(viewBounds);
        updatePathBounds(viewBounds);
        offsetIcon();

        if (needMirroring()) {
            // Mirror the drawable
            canvas.translate(component.getWidth(), 0f);
            canvas.scale(-1.0f, 1.0f);
        }
        final int dfValue = -1;
        if (roundedCornerRyPx > dfValue && roundedCornerRxPx > dfValue) {
            canvas.save();
            if (component != null) {
                canvas.translate(component.getPaddingLeft(), component.getPaddingTop());
            }
            if (drawBackgroundContour) {
                final float half = 2f;
                float halfContourSize = backgroundContourWidthPx / half;
                RectFloat rectF = new RectFloat(halfContourSize, halfContourSize,
                    viewBounds.getWidth() - halfContourSize, viewBounds.getHeight() - halfContourSize);
                canvas.drawRoundRect(rectF, roundedCornerRxPx, roundedCornerRyPx, backgroundBrush.getPaint());
                canvas.drawRoundRect(rectF, roundedCornerRxPx, roundedCornerRyPx, backgroundContourBrush.getPaint());
            } else {
                RectFloat rectF = new RectFloat(0f, 0f, viewBounds.getWidth(), viewBounds.getHeight());
                canvas.drawRoundRect(rectF, roundedCornerRxPx, roundedCornerRyPx, backgroundBrush.getPaint());
            }
            canvas.restore();
        }
        path.close();
        if (drawContour) {
            canvas.drawPath(path, contourBrush.getPaint());
        }
        if (iconColorFilter == null) {
            iconBrush.getPaint().setColorFilter(tintFilter);
        } else {
            iconBrush.getPaint().setColorFilter(iconColorFilter);
        }
        canvas.drawPath(path, iconBrush.getPaint());
    }

    /**
     * 设置着色
     *
     * @param tint 着色
     */
    public void setTintList(Map<Integer, Color> tint) {
        this.tint = tint;
    }

    /**
     * 设置着色模式
     *
     * @param tintMode 着色模式
     */
    public void setTintMode(BlendMode tintMode) {
        if (tintMode == null) {
            this.tintPorterMode = BlendMode.SRC_IN;
        } else {
            this.tintPorterMode = tintMode;
        }
    }

    @Override
    public void setBounds(Rect bounds) {
        super.setBounds(bounds);
        onBoundsChange(bounds);
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        onBoundsChange(new Rect(left, top, right, bottom));
    }

    /**
     * 获取画布尺寸
     *
     * @return 画笔尺寸
     */
    public Rect getCanvasBounds() {
        return canvasRect;
    }

    /**
     * 边界变化
     *
     * @param bounds 新的边界
     */
    public void onBoundsChange(Rect bounds) {
        if (component != null) {
            canvasRect = new Rect(0, 0, component.getWidth(), component.getHeight());
        } else {
            canvasRect = bounds;
        }
        updatePaddingBounds(bounds);
        updatePathBounds(bounds);
        offsetIcon();
        path.close();
    }

    /**
     * 是否具备状态属性
     *
     * @return 是否具备状态属性
     */
    public boolean isStateful() {
        return iconBrush.isStateful()
            || contourBrush.isStateful()
            || backgroundBrush.isStateful()
            || backgroundContourBrush.isStateful()
            || (tint != null && !tint.isEmpty());
    }

    /**
     * 设置状态
     *
     * @param state 状态
     * @return 是否设置成功
     */
    public boolean setState(int state) {
        this.state = state;
        boolean change = onStateChange(state);
        return change || iconBrush.isStateful()
            || contourBrush.isStateful()
            || backgroundBrush.isStateful()
            || backgroundContourBrush.isStateful()
            || (tint != null && !tint.isEmpty());
    }

    /**
     * 状态变化
     *
     * @param stateSet 新的状态
     * @return 是否变化
     */
    public boolean onStateChange(int stateSet) {
        boolean isNeedsRedraw = iconBrush.applyState(stateSet);
        isNeedsRedraw = contourBrush.applyState(stateSet) || isNeedsRedraw;
        isNeedsRedraw = backgroundBrush.applyState(stateSet) || isNeedsRedraw;
        isNeedsRedraw = backgroundContourBrush.applyState(stateSet) || isNeedsRedraw;

        if (tint != null) {
            updateTintFilter();
            isNeedsRedraw = true;
        }

        return isNeedsRedraw;
    }

    /**
     * 获取宽度
     *
     * @return 宽
     */
    public int getIntrinsicWidth() {
        return sizeXpx;
    }

    /**
     * 获取高度
     *
     * @return 高
     */
    public int getIntrinsicHeight() {
        return sizeYpx;
    }

    @Override
    public void setAlpha(int alpha) {
        iconBrush.setAlpha(alpha);
        contourBrush.setAlpha(alpha);
        backgroundBrush.setAlpha(alpha);
        backgroundContourBrush.setAlpha(alpha);
        compatAlpha = alpha;
    }

    @Override
    public int getAlpha() {
        return compatAlpha;
    }

    /**
     * 设置颜色过滤器
     *
     * @param cf 颜色过滤器
     */
    public void setColorFilter(ColorFilter cf) {
        iconColorFilter = cf;
    }

    /**
     * 删除颜色过滤器
     */
    public void clearColorFilter() {
        iconColorFilter = null;
    }

    /**
     * Update the Padding Bounds
     *
     * @param viewBounds 绘制边界
     */
    private void updatePaddingBounds(Rect viewBounds) {
        final int double2 = 2;
        if (paddingPx >= 0
            && paddingPx * double2 <= viewBounds.getWidth()
            && paddingPx * double2 <= viewBounds.getHeight()) {
            paddingBounds.set(viewBounds.left + paddingPx, viewBounds.top + paddingPx,
                viewBounds.right - paddingPx, viewBounds.bottom - paddingPx);
        }
    }

    /**
     * Update the TextSize
     *
     * @param viewBounds 绘制边界
     */
    private void updatePathBounds(Rect viewBounds) {
        String textValue;
        if (icon != null && icon.getCharacter() != null) {
            textValue = icon.getCharacter().toString();
        } else {
            textValue = iconText;
        }
        int textSize = paddingBounds.getHeight();
        iconBrush.getPaint().setTextSize(textSize);
        iconBrush.getPaint().addTextToPath(textValue, 0f, 0f, path);
        path.computeBounds(pathBounds);

        if (respectFontBounds) {
            path.offset(viewBounds.getCenterX(),
                paddingBounds.top + textSize - iconBrush.getPaint().getFontMetrics().descent);
        } else {
            float deltaWidth = paddingBounds.getWidth() * 1f / pathBounds.getWidth();
            float deltaHeight = paddingBounds.getHeight() * 1f / pathBounds.getHeight();
            float delta = Math.min(deltaWidth, deltaHeight);
            textSize *= delta;
            iconBrush.getPaint().setTextSize(textSize);
            iconBrush.getPaint().addTextToPath(textValue, 0f, 0f, path);
            path.computeBounds(pathBounds);
            path.offset(paddingBounds.left - pathBounds.left,
                paddingBounds.top - pathBounds.top);
        }
    }

    /**
     * Set the icon offset
     */
    private void offsetIcon() {
        if (respectFontBounds) {
            path.offset(iconOffsetXpx, iconOffsetYpx);
        } else {
            final int half = 2;
            float offsetX = (paddingBounds.getWidth() - pathBounds.getWidth()) / half;
            float offsetY = (paddingBounds.getHeight() - pathBounds.getHeight()) / half;
            path.offset(offsetX + iconOffsetXpx, offsetY + iconOffsetYpx);
        }
    }

    /**
     * Ensures the tint filter is consistent with the current tint color and mode.
     */
    private void updateTintFilter() {
        final Map<Integer, Color> tint2 = this.tint;
        final BlendMode tintMode = this.tintPorterMode;
        if (tint2 == null) {
            tintFilter = null;
            return;
        }
        Color color = tint2.get(state);
        if (color == null) {
            color = Color.TRANSPARENT;
        }
        tintFilter = new ColorFilter(color.getValue(), tintMode);
    }

    private boolean needMirroring() {
        return autoMirroredCompat && component != null && component.getComponentParent() != null
            && ((Component) component.getComponentParent()).getLayoutDirection() == Component.LayoutDirection.RTL;
    }

    /**
     * 刷新绘制
     */
    public void invalidateThis() {
        if (invalidationEnabled) {
            invalidateSelf();
        }
    }

    /**
     * Invalidates the `IconicsDrawable` if invalidation is currently enabled
     */
    public void invalidateSelf() {
        if (component != null) {
            component.invalidate();
        }
    }

    /**
     * Creates a BitMap to use in Widgets or anywhere else
     *
     * @return bitmap to set
     */
    public PixelMap toBitmap() {
        final int defaultValue = -1;
        if (sizeXpx == defaultValue || sizeYpx == defaultValue) {
            actionBar();
        }
        PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
        opts.size = new Size(getWidth(), getHeight());
        opts.editable = true;
        opts.pixelFormat = PixelFormat.ARGB_8888;
        PixelMap bitmap = PixelMap.create(opts);
        Canvas canvas = new Canvas(new Texture(bitmap));
        draw(canvas);
        return bitmap;
    }

    /**
     * 处理标题栏
     *
     * @return 当前drawable对象
     */
    public IconicsDrawable actionBar() {
        setSize(IconicsSize.toolbarIconSize(getContext()));
        setPaddingPx(IconicsSize.toolbarIconPadding(getContext()));
        return this;
    }

    /**
     * Transform the icon to an animated icon
     *
     * @return new IconicsDrawable with the same values.
     */
    public IconicsAnimatedDrawable toAnimatedDrawable() {
        IconicsAnimatedDrawable animatedDrawable = new IconicsAnimatedDrawable(component, theme);
        final int defaultValue = -1;
        IconicsDrawable copy = copy(component, animatedDrawable, theme, null, null, null,
            null, null, null, defaultValue, null, null,
            this.autoMirroredCompat, defaultValue, defaultValue, this.respectFontBounds, this.drawContour,
            this.drawBackgroundContour, defaultValue, defaultValue,
            defaultValue, defaultValue, defaultValue,
            defaultValue, defaultValue, defaultValue,
            defaultValue, defaultValue, null,
            null, null, null);
        return (IconicsAnimatedDrawable) copy;
    }

    /**
     * 拷贝drawable对象
     *
     * @return drawable对象
     */
    public IconicsDrawable copy() {
        return copy(this.component, this,
            this.theme, this.getColorList(),
            this.getStyle(), this.getTypeface(),
            this.getBackgroundContourColorList(),
            this.getBackgroundColorList(),
            this.getContourColorList(),
            this.compatAlpha, this.icon, this.iconText, this.autoMirroredCompat,
            this.sizeXpx, this.sizeYpx, this.respectFontBounds, this.drawContour,
            this.drawBackgroundContour, this.roundedCornerRxPx, this.roundedCornerRyPx,
            this.paddingPx, this.contourWidthPx, this.backgroundContourWidthPx,
            this.iconOffsetXpx, this.iconOffsetYpx, this.shadowRadiusPx,
            this.shadowDxPx, this.shadowDyPx, this.shadowColorInt,
            this.tint,
            this.tintPorterMode,
            this.iconColorFilter);
    }

    /**
     * Copies this IconicsDrawable with the given modifications
     *
     * @param component 绘制组件
     * @param target 拷贝目标对象
     * @param theme 主题
     * @param colorList icon颜色
     * @param style 样式
     * @param typeface 字体
     * @param backgroundContourColorList 背景线框颜色
     * @param backgroundColorList 背景颜色
     * @param contourColorList icon线框颜色
     * @param compatAlpha 透明度
     * @param icon 图标
     * @param iconText 图标文字
     * @param autoMirroredCompat 是否镜像
     * @param sizeXpx 宽
     * @param sizeYpx 高
     * @param respectFontBounds 是否适应字体内边距
     * @param drawContour 是否绘制线框
     * @param drawBackgroundContour 是否绘制背景线框
     * @param roundedCornerRxPx x圆角
     * @param roundedCornerRyPx y圆角
     * @param paddingPx icon内边距
     * @param contourWidthPx 线框宽度
     * @param backgroundContourWidthPx 背景线框宽度
     * @param iconOffsetXpx icon水平偏移量
     * @param iconOffsetYpx icon垂直偏移量
     * @param shadowRadiusPx 阴影圆角
     * @param shadowDxPx 阴影水平圆角
     * @param shadowDyPx 阴影垂直圆角
     * @param shadowColorInt 阴影着色
     * @param tint 是否着色
     * @param tintPorterMode 着色模式
     * @param iconColorFilter icon颜色过滤器
     * @return 拷贝的drawable
     */
    public IconicsDrawable copy(Component component, IconicsDrawable target,
                                Theme theme, Map<Integer, Color> colorList,
                                Paint.Style style, Font typeface,
                                Map<Integer, Color> backgroundContourColorList,
                                Map<Integer, Color> backgroundColorList,
                                Map<Integer, Color> contourColorList,
                                int compatAlpha, IIcon icon, String iconText, boolean autoMirroredCompat,
                                int sizeXpx, int sizeYpx, boolean respectFontBounds, boolean drawContour,
                                boolean drawBackgroundContour, float roundedCornerRxPx, float roundedCornerRyPx,
                                int paddingPx, int contourWidthPx, int backgroundContourWidthPx,
                                int iconOffsetXpx, int iconOffsetYpx, float shadowRadiusPx,
                                float shadowDxPx, float shadowDyPx, Color shadowColorInt,
                                Map<Integer, Color> tint,
                                BlendMode tintPorterMode,
                                ColorFilter iconColorFilter) {
        final int defaultValue = -1;
        Theme theme1 = theme;
        if (theme == null) {
            theme1 = this.theme;
        }
        Map<Integer, Color> colorList1 = colorList;
        if (colorList == null) {
            colorList1 = this.getColorList();
        }
        Paint.Style style1 = style;
        if (style == null) {
            style1 = this.getStyle();
        }
        Font typeface1 = typeface;
        if (typeface == null) {
            typeface1 = this.getTypeface();
        }
        Map<Integer, Color> backgroundContourColorList1 = backgroundContourColorList;
        if (backgroundContourColorList == null) {
            backgroundContourColorList1 = this.getBackgroundContourColorList();
        }
        Map<Integer, Color> backgroundColorList1 = backgroundColorList;
        if (backgroundColorList == null) {
            backgroundColorList1 = this.getBackgroundColorList();
        }
        Map<Integer, Color> contourColorList1 = contourColorList;
        if (contourColorList == null) {
            contourColorList1 = this.getContourColorList();
        }
        int compatAlpha1 = compatAlpha;
        if (compatAlpha == defaultValue) {
            compatAlpha1 = this.compatAlpha;
        }
        IIcon icon1 = icon;
        if (icon == null) {
            icon1 = this.icon;
        }
        String iconText1 = iconText;
        if (iconText == null) {
            iconText1 = this.iconText;
        }
        int sizeXpx1 = sizeXpx;
        if (sizeXpx == defaultValue) {
            sizeXpx1 = this.sizeXpx;
        }
        int sizeYpx1 = sizeYpx;
        if (sizeYpx == defaultValue) {
            sizeYpx1 = this.sizeYpx;
        }
        float roundedCornerRxPx1 = roundedCornerRxPx;
        if (roundedCornerRxPx == defaultValue) {
            roundedCornerRxPx1 = this.roundedCornerRxPx;
        }
        float roundedCornerRyPx1 = roundedCornerRyPx;
        if (roundedCornerRyPx == defaultValue) {
            roundedCornerRyPx1 = this.roundedCornerRyPx;
        }
        int paddingPx1 = paddingPx;
        if (paddingPx == defaultValue) {
            paddingPx1 = this.paddingPx;
        }
        int contourWidthPx1 = contourWidthPx;
        if (contourWidthPx == defaultValue) {
            contourWidthPx1 = this.contourWidthPx;
        }
        int backgroundContourWidthPx1 = backgroundContourWidthPx;
        if (backgroundContourWidthPx == defaultValue) {
            backgroundContourWidthPx1 = this.backgroundContourWidthPx;
        }
        int iconOffsetXpx1 = iconOffsetXpx;
        if (iconOffsetXpx == defaultValue) {
            iconOffsetXpx1 = this.iconOffsetXpx;
        }
        int iconOffsetYpx1 = iconOffsetYpx;
        if (iconOffsetYpx == defaultValue) {
            iconOffsetYpx1 = this.iconOffsetYpx;
        }
        float shadowRadiusPx1 = shadowRadiusPx;
        if (shadowRadiusPx == defaultValue) {
            shadowRadiusPx1 = this.shadowRadiusPx;
        }
        float shadowDxPx1 = shadowDxPx;
        if (shadowDxPx == defaultValue) {
            shadowDxPx1 = this.shadowDxPx;
        }
        float shadowDyPx1 = shadowDyPx;
        if (shadowDyPx == defaultValue) {
            shadowDyPx1 = this.shadowDyPx;
        }
        Color shadowColorInt1 = shadowColorInt;
        if (shadowColorInt == null) {
            shadowColorInt1 = this.shadowColorInt;
        }
        BlendMode tintPorterMode1 = tintPorterMode;
        if (tintPorterMode == null) {
            tintPorterMode1 = this.tintPorterMode;
        }
        ColorFilter iconColorFilter1 = iconColorFilter;
        if (iconColorFilter == null) {
            iconColorFilter1 = this.iconColorFilter;
        }
        final Map<Integer, Color> finalColorList = colorList1;
        final Paint.Style finalStyle = style1;
        final Font finalTypeface = typeface1;
        final int finalCompatAlpha = compatAlpha1;
        final IIcon finalIcon = icon1;
        final String finalIconText = iconText1;
        final int finalSizeXpx = sizeXpx1;
        final int finalSizeYpx = sizeYpx1;
        final float finalRoundedCornerRxPx = roundedCornerRxPx1;
        final float finalRoundedCornerRyPx = roundedCornerRyPx1;
        final int finalPaddingPx = paddingPx1;
        final int finalContourWidthPx = contourWidthPx1;
        final int finalBackgroundContourWidthPx = backgroundContourWidthPx1;
        final int finalIconOffsetXpx = iconOffsetXpx1;
        final int finalIconOffsetYpx = iconOffsetYpx1;
        final float finalShadowRadiusPx = shadowRadiusPx1;
        final float finalShadowDxPx = shadowDxPx1;
        final float finalShadowDyPx = shadowDyPx1;
        final Color finalShadowColorInt = shadowColorInt1;
        final BlendMode finalTintPorterMode = tintPorterMode1;
        final ColorFilter finalIconColorFilter = iconColorFilter1;
        final Map<Integer, Color> finalBackgroundContourColorList = backgroundContourColorList1;
        final Map<Integer, Color> finalBackgroundColorList = backgroundColorList1;
        final Map<Integer, Color> finalContourColorList = contourColorList1;
        IconicsDrawable target1 = target;
        if (target == null) {
            target1 = new IconicsDrawable(component, theme);
        }
        return KtExtensions.apply(target1, drawable -> {
            drawable.setColorList(finalColorList);
            drawable.setStyle(finalStyle);
            drawable.setTypeface(finalTypeface);
            drawable.setBackgroundContourColorList(finalBackgroundContourColorList);
            drawable.setBackgroundColorList(finalBackgroundColorList);
            drawable.setContourColorList(finalContourColorList);
            drawable.compatAlpha = finalCompatAlpha;
            drawable.icon = finalIcon;
            drawable.iconText = finalIconText;
            drawable.autoMirroredCompat = autoMirroredCompat;
            drawable.sizeXpx = finalSizeXpx;
            drawable.sizeYpx = finalSizeYpx;
            drawable.respectFontBounds = respectFontBounds;
            drawable.drawContour = drawContour;
            drawable.drawBackgroundContour = drawBackgroundContour;
            drawable.roundedCornerRxPx = finalRoundedCornerRxPx;
            drawable.roundedCornerRyPx = finalRoundedCornerRyPx;
            drawable.paddingPx = finalPaddingPx;
            drawable.contourWidthPx = finalContourWidthPx;
            drawable.backgroundContourWidthPx = finalBackgroundContourWidthPx;
            drawable.iconOffsetXpx = finalIconOffsetXpx;
            drawable.iconOffsetYpx = finalIconOffsetYpx;
            drawable.shadowRadiusPx = finalShadowRadiusPx;
            drawable.shadowDxPx = finalShadowDxPx;
            drawable.shadowDyPx = finalShadowDyPx;
            drawable.shadowColorInt = finalShadowColorInt;
            drawable.tint = tint;
            drawable.tintPorterMode = finalTintPorterMode;
            drawable.iconColorFilter = finalIconColorFilter;
        });
    }

    /**
     * 代码块回调
     *
     * @since 2021-06-04
     */
    public interface IconicsDrawableKtBlock {
        /**
         * 执行代码
         *
         * @param invoker 调用者
         */
        void invoke(IconicsDrawable invoker);
    }

    /**
     * Applies the shadow in an optimized form. Will disable shadow invalidation for the inner shadow set operations
     *
     * @param invoker 调用者
     * @param block 代码回调
     * @return drawable
     */
    public IconicsDrawable applyShadow(IconicsDrawable invoker, IconicsDrawableKtBlock block) {
        invalidateShadowEnabled = false;
        block.invoke(invoker);
        invalidateShadowEnabled = true;
        updateShadow();
        return invoker;
    }

    /**
     * Applies properties in an optimized form. Will disable invalidation of the IconicsDrawable
     * for the inner property set operations
     *
     * @param invoker 调用者
     * @param block 代码回调
     * @return drawable
     */
    public IconicsDrawable apply(IconicsDrawable invoker, IconicsDrawableKtBlock block) {
        invalidationEnabled = false;
        block.invoke(invoker);
        invalidationEnabled = true;
        invalidateSelf();
        return invoker;
    }

    /**
     * Loads and draws given text
     *
     * @param icon 图标名
     * @return The current IconicsDrawable for chaining.
     */
    public IconicsDrawable icon(String icon) {
        if (!Iconics.getInstance().isInitDone()) {
            HiLog.error(LABEL, ERROR_MESSAGE);
        }
        ITypeface typeface = Iconics.getInstance().findFont(Iconics.iconPrefix(icon));
        if (typeface != null) {
            IIcon icon1 = typeface.getIcon(Iconics.clearedIconName(icon));
            this.icon(icon1);
        }
        return this;
    }

    /**
     * Loads and draws given icon
     *
     * @param icon 图标
     * @return The current IconicsDrawable for chaining.
     */
    public IconicsDrawable icon(IIcon icon) {
        if (!Iconics.getInstance().isInitDone()) {
            final int domain = 110;
            HiLog.error(LABEL, ERROR_MESSAGE);
        }
        setIcon(icon);
        return this;
    }

    /**
     * Loads and draws given icon
     *
     * @param typeface 字体名
     * @param icon 图标名
     * @return The current IconicsDrawable for chaining.
     */
    public IconicsDrawable icon(ITypeface typeface, IIcon icon) {
        if (!Iconics.getInstance().isInitDone()) {
            final int domain = 110;
            HiLog.error(LABEL, ERROR_MESSAGE);
        }
        iconBrush.getPaint().setFont(typeface.getRawTypeface(this.getContext()));
        setIcon(icon);
        return this;
    }

    /**
     * Loads and draws given char
     *
     * @param icon 图标名
     * @return The current IconicsDrawable for chaining.
     */
    public IconicsDrawable icon(Character icon) {
        setIconText(icon.toString());
        return this;
    }

    /**
     * Loads and draws given text
     *
     * @param icon 图标名
     * @param typeface 字体名
     * @return The current IconicsDrawable for chaining.
     */
    public IconicsDrawable iconText(String icon, Font typeface) {
        iconBrush.getPaint().setFont(typeface);
        setIconText(icon);
        return this;
    }
}
